import axios from 'axios';
import type {AxiosResponse, AxiosError, AxiosInstance, AxiosRequestConfig} from 'axios';
import {REFRESH_TOKEN_CODE} from '@/config';
import {
	localStg,
	handleAxiosError,
	handleBackendError,
	handleResponseError,
	handleServiceResult,
	transformRequestData,
	handleServiceErrorResult
} from '@/utils';
import {handleRefreshToken} from './helpers';

type RefreshRequestQueue = (config: AxiosRequestConfig) => void;

/**
 * 封装axios请求类
 * @author Soybean<honghuangdc@gmail.com>
 */
export default class CustomAxiosInstance {
	instance: AxiosInstance;

	backendConfig: Service.BackendResultConfig;

	isRefreshing: boolean;

	retryQueues: RefreshRequestQueue[];

	/**
	 * 初始化请求实例
	 * @param axiosConfig - axios配置
	 * @param backendConfig - 后端返回的数据配置
	 */
	constructor(
		axiosConfig: AxiosRequestConfig,
		backendConfig: Service.BackendResultConfig = {
			codeKey: 'code',
			dataKey: 'data',
			msgKey: 'msg',
			successCode: 0
		}
	) {
		this.backendConfig = backendConfig;
		this.instance = axios.create(axiosConfig);
		this.setInterceptor();
		this.isRefreshing = false;
		this.retryQueues = [];
	}

	/** 设置请求拦截器 */
	setInterceptor() {
		this.instance.interceptors.request.use(
			async config => {
				const handleConfig = {...config};
				if (handleConfig.headers) {
					// 数据转换
					const contentType = handleConfig.headers['Content-Type'] as UnionKey.ContentType;
					handleConfig.data = await transformRequestData(handleConfig.data, contentType);
					// 设置token
					handleConfig.headers.Authorization = localStg.get('token') || '';
				}
				return handleConfig;
			},
			(axiosError: AxiosError) => {
				const error = handleAxiosError(axiosError);
				return handleServiceErrorResult(error);
			}
		);
		this.instance.interceptors.response.use(
			(async response => {
				const {status, config} = response;
				if (status === 200 || status < 300 || status === 304) {
					const backend = response.data;
					const {codeKey, successCode} = this.backendConfig;
					// 响应头包含 token 就说明后端返回了新的 token
					if (response.headers.authorization) {
						config.headers.Authorization = response.headers?.Authorization;
						localStg.set('token', response.headers.authorization as string);
						localStg.set('refreshToken', response.headers['refresh-authorization'] as string);
					}
					// 请求成功
					if (backend[codeKey] === successCode) {
						return handleServiceResult(backend);
					}
					// token失效, 刷新token
					if (REFRESH_TOKEN_CODE.includes(backend[codeKey])) {
						// 原始请求
						const originRequest =
							new Promise(resolve => {
								this.retryQueues.push(
									(refreshConfig: AxiosRequestConfig) => {
										config.headers.Authorization = refreshConfig.headers?.Authorization;
										resolve(this.instance.request(config));
									}
								);
							});

						if (!this.isRefreshing) {
							this.isRefreshing = true;
							const refreshConfig = await handleRefreshToken(response.config);
							if (refreshConfig) {
								this.retryQueues.map(cb => cb(refreshConfig));
							}
							this.retryQueues = [];
							this.isRefreshing = false;
						}
						return originRequest;
					}

					const error = handleBackendError(backend, this.backendConfig);
					return handleServiceErrorResult(error);
				}
				const error = handleResponseError(response);
				return handleServiceErrorResult(error);
			}) as (response: AxiosResponse<any, any>) => Promise<AxiosResponse<any, any>>,
			(axiosError: AxiosError) => {
				const error = handleAxiosError(axiosError);
				return handleServiceErrorResult(error);
			}
		);
	}
}
